package com.heima.wemedia.service.impl;

import com.alibaba.fastjson.JSON;
import com.baomidou.mybatisplus.core.toolkit.Wrappers;
import com.heima.apis.article.IArticleClient;
import com.heima.common.aliyun.GreenImageScan;
import com.heima.common.aliyun.GreenTextScan;
import com.heima.common.exception.CustomException;
import com.heima.common.tess4j.Tess4jClient;
import com.heima.file.service.FileStorageService;
import com.heima.model.article.dtos.ArticleDto;
import com.heima.model.common.dtos.ResponseResult;
import com.heima.model.common.enums.AppHttpCodeEnum;
import com.heima.model.wemedia.pojos.WmNews;
import com.heima.model.wemedia.pojos.WmSensitive;
import com.heima.utils.common.SensitiveWordUtil;
import com.heima.wemedia.builder.ArticleDtoBuilder;
import com.heima.wemedia.mapper.WmChannelMapper;
import com.heima.wemedia.mapper.WmNewsMapper;
import com.heima.wemedia.mapper.WmSensitiveMapper;
import com.heima.wemedia.mapper.WmUserMapper;
import com.heima.wemedia.service.WmNewsAutoScanService;
import io.seata.spring.annotation.GlobalTransactional;
import org.apache.commons.lang.StringUtils;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.scheduling.annotation.Async;
import org.springframework.stereotype.Service;
import org.springframework.transaction.annotation.Transactional;

import javax.imageio.ImageIO;
import java.awt.image.BufferedImage;
import java.io.ByteArrayInputStream;
import java.util.*;
import java.util.stream.Collectors;
@Service
@Transactional
public class WmNewsAutoScanServiceImpl implements WmNewsAutoScanService {
    @Autowired
    private WmNewsMapper newsMapper;
    @Autowired
    private GreenTextScan greenTextScan;
    @Autowired
    private FileStorageService fileStorageService;
    @Autowired
    private GreenImageScan greenImageScan;

    @Autowired
    private WmUserMapper wmUserMapper;

    @Autowired
    private WmChannelMapper channelMapper;

    @Autowired
    private IArticleClient articleClient;

    @Autowired
    private WmSensitiveMapper sensitiveMapper;

    @Autowired
    private Tess4jClient tess4jClient;
    /**
     * 自媒体文章审核
     *
     * @param id 自媒体文章id
     * @throws Exception
     */
    @Override
    @Async  //标明异步调用
    @GlobalTransactional
    public void autoScanWmNews(Integer id) throws Exception {
        //1. 根据文章的id查询文章
        WmNews wmNews = newsMapper.selectById(id);
        //2.如果没有查询到文章则抛出异常
        if (null == wmNews) {
            throw new CustomException(AppHttpCodeEnum.DATA_NOT_EXIST);
        }
        //文章进行审核的时候，只有状态位1才需要进行审核，其他状态都不需要审核
        if (wmNews.getStatus().equals(WmNews.Status.SUBMIT.getCode())){
            //3.解析文章的内容数据
            Map<String, Object> data = handleTextAndImages(wmNews);

            //自定义敏感词审核
            boolean status = handleSensitiveScan(data.get("text").toString(), wmNews);
            if (!status){
                return;
            }

            //4.把文章内容上传到阿里云进行审核---如果审核失败，文章的状态改为2，人工审核则为3
             status = handleTextScan(data.get("text").toString(), wmNews);
            //文章审核为false则直接返回
            if (!status) {
                return;
            }
            //5.把所有图片下载下来
            //6.把所有图片的二进制文件上传到阿里云进行图片审核，如果审核失败，该文章状态改为2，人工审核则为3
            status = handleImageScan((List<String>) data.get("images"), wmNews);
            //图片审核为false则直接返回
            if (!status){
                return;
            }

            //7.同步文章数据到微服务
            ResponseResult responseResult = saveAppArticle(wmNews);

             // int i = 1/0;
            if (responseResult.getCode() != 200){
                throw new CustomException(AppHttpCodeEnum.DATA_SYNC_FAIL);
            }
            //8.更新文章的状态位位9
            updateWmNew(wmNews,WmNews.Status.PUBLISHED,"审核通过");
        }
    }

    /**
     * 自定义敏感词审核管理
     * @param text
     * @param wmNews
     * @return
     */
    private boolean handleSensitiveScan(String text, WmNews wmNews) {
        Boolean flag = Boolean.TRUE;
        //1.查询所有的敏感词对象
        List<WmSensitive> wmSensitiveList = sensitiveMapper.selectList(Wrappers.<WmSensitive>lambdaQuery().select(WmSensitive::getSensitives));
        //2.得到所有敏感词字符串集合
        List<String> list = wmSensitiveList.stream().map(item -> item.getSensitives()).collect(Collectors.toList());
        //3.初始化DFA算法的大 map 键值对
        SensitiveWordUtil.initMap(list);
        //4.使用DFA算法，完成文本的审核
        Map<String, Integer> map = SensitiveWordUtil.matchWords(text);
        //5.如果审核失败，则更新文章的审核状态位为2，并且给出失败原因
        if (null != map && map.size() > 0){
            updateWmNew(wmNews,WmNews.Status.FAIL,"文章审核失败，其中包含敏感词" + map);
            flag = Boolean.FALSE;
        }
        return flag;
    }

    /**
     * 数据通过自媒体端，同步到文章微服务中
     * @param wmNews
     */
    private ResponseResult saveAppArticle(WmNews wmNews) {
        /*ArticleDto dto = new ArticleDto();
        BeanUtils.copyProperties(wmNews,dto);

        //单独处理作者相关的数据
        if (wmNews.getUserId() != null){
            WmUser wmUser = wmUserMapper.selectById(wmNews.getUserId());
            dto.setAuthorId((long)wmUser.getApUserId());
            dto.setAuthorName(wmUser.getName());
        }
        //单独处理频道相关的数据
        if(null != wmNews.getChannelId()){
            WmChannel wmChannel = channelMapper.selectById(wmNews.getChannelId());
            dto.setChannelName(wmChannel.getName());
        }
        //其他散列数据
        dto.setLayout(wmNews.getType());*/
        ArticleDto dto = new ArticleDtoBuilder(wmNews,wmUserMapper,channelMapper)
                .buildBeanCopy()
                .buildAuthor()
                .buildChannel()
                .buildBasic()
                .builder();

        ResponseResult responseResult = articleClient.saveArticle(dto);
        return responseResult;
    }

    /**
     * 图片检测
     *
     * @param images
     * @param wmNews
     * @return
     */
    private boolean handleImageScan(List<String> images, WmNews wmNews) throws Exception {
        Boolean flag = Boolean.TRUE;
        if (images.size() == 0) {
            return flag;
        }
        List<byte[]> imageList = new ArrayList<>();
        //1.对图片进行去重
        images = images.stream().distinct().collect(Collectors.toList());
        //2.下载每一个图片
        for (String url : images) {
            byte[] bytes = fileStorageService.downLoadFile(url);
            imageList.add(bytes);
            //下载一张图片，识别一张图片并审核一张图片的内容
            ByteArrayInputStream in = new ByteArrayInputStream(bytes);
            BufferedImage image = ImageIO.read(in);
            String str = tess4jClient.doOCR(image);
            boolean status = handleSensitiveScan(str, wmNews);
            if (!status){
                flag = Boolean.FALSE;
                return flag;
            }
        }
        Map map = greenImageScan.imageScan(imageList);

        //如果返回值等于block表示审核失败-审核文章的状态位2 并给出审核失败的原因
        if ("block".equals(map.get("suggestion"))){
            updateWmNew(wmNews,WmNews.Status.FAIL,"图片存在违规内容，审核失败");
            flag = Boolean.FALSE;
        }else if ("review".equals(map.get("suggestion"))){
            updateWmNew(wmNews,WmNews.Status.ADMIN_AUTH,"文章审核失败，图片中包含了不确定的内容，需要人工审核");
            flag = Boolean.FALSE;
        }

        return flag;
    }

    /**
     * 处理文本
     *
     * @param text
     * @param wmNews
     */
    private boolean handleTextScan(String text, WmNews wmNews) throws Exception {

        Boolean flag = Boolean.TRUE;
        if (StringUtils.isEmpty(text)) {
            return flag;
        }
        Map map = greenTextScan.greeTextScan(text);
        //如果返回值等于block则表示审核失败，审核文章的状态位为2，并给出审核失败的原因
        if ("block".equals(map.get("suggestion"))) {
            updateWmNew(wmNews, WmNews.Status.FAIL, "文章审核失败，里面包含了不确定的文本内容");
            flag = Boolean.FALSE;

        } else if ("review".equals(map.get("suggestion"))) {
            updateWmNew(wmNews, WmNews.Status.ADMIN_AUTH, "文章审核失败，里面包含了不缺定内容，需人工审核");
            flag = Boolean.FALSE;
        }

        return flag;
    }

    /**
     * 文章审核通过
     *
     * @param wmNews
     * @param published
     * @param reason
     */
    private void updateWmNew(WmNews wmNews, WmNews.Status published, String reason) {
        wmNews.setStatus(published.getCode());
        wmNews.setReason(reason);
        newsMapper.updateById(wmNews);
    }

    /**
     * 抽取文章中所有的内容和图片
     *
     * @param wmNews
     */
    private Map<String, Object> handleTextAndImages(WmNews wmNews) {
        //使用容器来分别装解析出来的内容和图片
        StringBuilder sb = new StringBuilder();

        List<String> imageUrls = new ArrayList<>();

        //解析文章中的文本和图片
        if (StringUtils.isNotBlank(wmNews.getContent())) {
            List<Map> list = JSON.parseArray(wmNews.getContent(), Map.class);
            for (Map map : list) {
                if ("image".equals(map.get("type"))) {
                    imageUrls.add(map.get("value").toString());
                } else {
                    //为文字
                    sb.append(map.get("value").toString());
                }
            }
        }
        //处理封面中的图片
        if (StringUtils.isNotBlank(wmNews.getImages())) {
            String[] strs = wmNews.getImages().split(",");
            imageUrls.addAll(Arrays.asList(strs));
        }

        //处理标题中的文本
        sb.append(wmNews.getTitle());
        HashMap<String, Object> map = new HashMap<>();

        map.put("text", sb.toString());
        map.put("images", imageUrls);

        return map;
    }
}
